package ynu.edu.pay.service.impl;

import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import ynu.edu.api.client.UserClient;
import ynu.edu.common.domain.DeductDTO;
import ynu.edu.common.exception.BizIllegalException;
import ynu.edu.common.utils.BeanUtils;
import ynu.edu.common.utils.UserContext;
import ynu.edu.pay.domain.dto.PayApplyDTO;
import ynu.edu.pay.domain.dto.PayOrderFormDTO;
import ynu.edu.pay.domain.po.PayOrder;
import ynu.edu.pay.enums.PayStatus;
import ynu.edu.pay.mapper.PayOrderMapper;
import ynu.edu.pay.service.IPayOrderService;

import java.time.LocalDateTime;
import java.util.List;


@Service
@RequiredArgsConstructor
@Slf4j
public class PayOrderServiceImpl extends ServiceImpl<PayOrderMapper, PayOrder> implements IPayOrderService {

    private final UserClient userClient;

    private final RabbitTemplate rabbitTemplate;

    @Override
    public String applyPayOrder(PayApplyDTO applyDTO) {
        log.info("开始创建支付单，业务订单号: {}", applyDTO.getBizOrderNo());
        // 1.幂等性校验
        PayOrder payOrder = checkIdempotent(applyDTO);
        log.info("支付单创建成功，支付单ID: {}", payOrder.getId());
        // 2.返回结果
        return payOrder.getId().toString();
    }

    @Override
    @Transactional
    public void tryPayOrderByBalance(PayOrderFormDTO payOrderFormDTO) {
        log.info("开始余额支付，支付单ID: {}", payOrderFormDTO.getId());
        
        // 1.查询支付单
        PayOrder po = getById(payOrderFormDTO.getId());
        if (po == null) {
            log.error("支付单不存在，支付单ID: {}", payOrderFormDTO.getId());
            
            // 添加调试信息：查询所有支付单
            List<PayOrder> allPayOrders = list();
            log.error("当前数据库中所有支付单: {}", 
                    allPayOrders.stream()
                            .map(order -> String.format("ID:%d, 业务订单号:%d, 状态:%d", 
                                    order.getId(), order.getBizOrderNo(), order.getStatus()))
                            .collect(java.util.stream.Collectors.toList()));
            
            throw new BizIllegalException("支付单不存在");
        }
        log.info("查询到支付单，状态: {}, 业务订单号: {}, 金额: {}", 
                po.getStatus(), po.getBizOrderNo(), po.getAmount());
        
        // 2.判断状态
        if(!PayStatus.WAIT_BUYER_PAY.equalsValue(po.getStatus())){
            // 订单不是未支付，状态异常
            log.warn("支付单状态异常，当前状态: {}", po.getStatus());
            throw new BizIllegalException("交易已支付或关闭！");
        }
        
        // 3.尝试扣减余额
        log.info("开始扣减余额，金额: {}", po.getAmount());
        
        // 设置用户上下文，确保Feign调用时能传递用户信息
        if (po.getBizUserId() != null) {
            UserContext.setUser(po.getBizUserId());
            log.info("设置用户上下文，用户ID: {}", po.getBizUserId());
        } else {
            log.error("支付单用户ID为空，无法进行余额支付");
            throw new BizIllegalException("支付单用户信息异常");
        }
        
        try {
            DeductDTO deductDTO = new DeductDTO();
            deductDTO.setUserId(po.getBizUserId());
            deductDTO.setAmount(po.getAmount());
            userClient.deductMoney(deductDTO);
        } finally {
            // 清理用户上下文
            UserContext.removeUser();
        }
        
        // 4.修改支付单状态
        boolean success = markPayOrderSuccess(payOrderFormDTO.getId(), LocalDateTime.now());
        if (!success) {
            log.error("修改支付单状态失败，支付单ID: {}", payOrderFormDTO.getId());
            throw new BizIllegalException("交易已支付或关闭！");
        }
        
        // 5.修改订单状态
        try {
            //发送消息到mq修改支付单状态
//            rabbitTemplate.convertAndSend("pay.topic", "pay.success", po.getBizOrderNo());
            //发送消息到mq修改订单状态
            rabbitTemplate.convertAndSend(
                    "trade.delay.direct",
                    "delay.order.query",
                    po.getBizOrderNo(),
                    message -> {
                        message.getMessageProperties().setDelay(900000); //15分钟延迟，检查订单支付状态
                        return message;
                    });

            log.info("支付成功消息发送成功，业务订单号: {}", po.getBizOrderNo());
        } catch (Exception e) {
            log.error("支付成功的消息发送失败，支付单id：{}， 交易单id：{}", po.getId(), po.getBizOrderNo(), e);
        }
    }

    /**
     * 修改支付单状态为支付成功
     *
     * @param id          支付单id
     * @param successTime 支付成功时间
     * @return 是否修改成功
     */
    public boolean markPayOrderSuccess(Long id, LocalDateTime successTime) {
        return lambdaUpdate()
                .set(PayOrder::getStatus, PayStatus.TRADE_SUCCESS.getValue())
                .set(PayOrder::getPaySuccessTime, successTime)
                .eq(PayOrder::getId, id)
                // 支付状态的乐观锁判断
                .in(PayOrder::getStatus, PayStatus.NOT_COMMIT.getValue(), PayStatus.WAIT_BUYER_PAY.getValue())
                .update();
    }


    private PayOrder checkIdempotent(PayApplyDTO applyDTO) {
        // 1.首先查询支付单
        PayOrder oldOrder = queryByBizOrderNo(applyDTO.getBizOrderNo());
        // 2.判断是否存在
        if (oldOrder == null) {
            // 不存在支付单，说明是第一次，写入新的支付单并返回
            PayOrder payOrder = buildPayOrder(applyDTO);
            payOrder.setPayOrderNo(IdWorker.getId());
            save(payOrder);
            return payOrder;
        }
        // 3.旧单已经存在，判断是否支付成功
        if (PayStatus.TRADE_SUCCESS.equalsValue(oldOrder.getStatus())) {
            // 已经支付成功，抛出异常
            throw new BizIllegalException("订单已经支付！");
        }
        // 4.旧单已经存在，判断是否已经关闭
        if (PayStatus.TRADE_CLOSED.equalsValue(oldOrder.getStatus())) {
            // 已经关闭，抛出异常
            throw new BizIllegalException("订单已关闭");
        }
        // 5.旧单已经存在，判断支付渠道是否一致
        if (!StringUtils.equals(oldOrder.getPayChannelCode(), applyDTO.getPayChannelCode())) {
            // 支付渠道不一致，需要重置数据，然后重新申请支付单
            PayOrder payOrder = buildPayOrder(applyDTO);
            payOrder.setId(oldOrder.getId());
            payOrder.setQrCodeUrl("");
            updateById(payOrder);
            payOrder.setPayOrderNo(oldOrder.getPayOrderNo());
            return payOrder;
        }
        // 6.旧单已经存在，且可能是未支付或未提交，且支付渠道一致，直接返回旧数据
        return oldOrder;
    }

    private PayOrder buildPayOrder(PayApplyDTO payApplyDTO) {
        // 1.数据转换
        PayOrder payOrder = BeanUtils.toBean(payApplyDTO, PayOrder.class);
        // 2.初始化数据
        payOrder.setPayOverTime(LocalDateTime.now().plusMinutes(120L));
        payOrder.setStatus(PayStatus.WAIT_BUYER_PAY.getValue());
        payOrder.setBizUserId(UserContext.getUser());
        return payOrder;
    }
    public PayOrder queryByBizOrderNo(Long bizOrderNo) {
        return lambdaQuery()
                .eq(PayOrder::getBizOrderNo, bizOrderNo)
                .one();
    }

}
